﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Octopus.Logging.Impl.Text.Common.IO;
using Octopus.Logging.Impl.Text.Common;
using Octopus.Logging.Commons;

namespace Octopus.Logging.Impl.Text
{
    public class FileAppender : TextWriterAppender
    {
        #region 私有字段

        /// <summary>
        /// Flag to indicate if we should append to the file
        /// or overwrite the file. The default is to append.
        /// </summary>
        private bool m_appendToFile = true;

        /// <summary>
        /// The name of the log file.
        /// </summary>
        private string m_fileName = null;

        /// <summary>
        /// The encoding to use for the file stream.
        /// </summary>
        private Encoding m_encoding = Encoding.UTF8;
        #endregion Private Instance Fields

        #region 构造器
        public FileAppender()
        {
        }

        /// <summary>
        /// Construct a new appender using the layout and file specified.
        /// The file will be appended to.
        /// </summary>
        /// <param name="layout">the layout to use with this appender</param>
        /// <param name="filename">the full path to the file to write to</param>
        public FileAppender(string filename)
            : this(filename, true)
        {
        }

        /// <summary>
        /// Construct a new appender using the layout, file and append mode.
        /// </summary>
        /// <param name="layout">the layout to use with this appender</param>
        /// <param name="filename">the full path to the file to write to</param>
        /// <param name="append">flag to indicate if the file should be appended to</param>
        public FileAppender(string filename, bool append)
        {
            OpenFile(filename, append);
        }
        #endregion Public Instance Constructors

        #region 公开属性

        /// <summary>
        /// Gets or sets the path to the file that logging will be written to.
        /// </summary>
        /// <value>
        /// The path to the file that logging will be written to.
        /// </value>
        /// <remarks>
        /// <para>
        /// If the path is relative it is taken as relative from 
        /// the application base directory.
        /// </para>
        /// </remarks>
        virtual public string File
        {
            get { return m_fileName; }
            set { m_fileName = ConvertToFullPath(value.Trim()); }
        }

        /// <summary>
        /// Gets or sets a flag that indicates weather the file should be
        /// appended to or overwritten.
        /// </summary>
        /// <value>
        /// Indicates whether the file should be appended to or overwritten.
        /// </value>
        /// <remarks>
        /// <para>
        /// If the value is set to false then the file will be overwritten, if 
        /// it is set to true then the file will be appended to.
        /// </para>
        /// The default value is true.
        /// </remarks>
        public bool AppendToFile
        {
            get { return m_appendToFile; }
            set { m_appendToFile = value; }
        }

        /// <summary>
        /// Gets or sets <see cref="Encoding"/> used to write to the file.
        /// </summary>
        /// <value>
        /// The <see cref="Encoding"/> used to write to the file.
        /// </value>
        public Encoding Encoding
        {
            get { return m_encoding; }
            set { m_encoding = value; }
        }


        #endregion Public Instance Properties

        #region 重写 AppenderSkeleton

        /// <summary>
        /// Activate the options on the file appender. This will
        /// case the file to be opened.
        /// </summary>
        override public void ActivateOptions()
        {
            base.ActivateOptions();
            if (m_fileName != null)
            {
                // We must cache the params locally because OpenFile will call
                // Reset which will clear the class fields. We need to remember the
                // values in case of an error.

                string fileName = m_fileName;
                bool appendToFile = m_appendToFile;

                try
                {
                    OpenFile(fileName, appendToFile);
                }
                catch
                {
                    throw;
                }
            }
            else
            {
                throw new Exception("FileAppender Params is not whole");
            }
        }

        #endregion Override implementation of AppenderSkeleton

        #region 重写 TextWriterAppender

        /// <summary>
        /// Closes any previously opened file and calls the parent's <see cref="TextWriterAppender.Reset"/>.
        /// </summary>
        override protected void Reset()
        {
            base.Reset();
            m_fileName = null;
        }

        #endregion Override implementation of TextWriterAppender

        #region 内部实现
        /// <summary>
        /// Closes the previously opened file.
        /// </summary>
        protected void CloseFile()
        {
            CloseWriter();
        }

        /// <summary>
        /// Sets and <i>opens</i> the file where the log output will
        /// go. The specified file must be writable.
        /// </summary>
        /// <param name="fileName">The path to the log file</param>
        /// <param name="append">If true will append to fileName. Otherwise will truncate fileName</param>
        /// <remarks>
        /// <para>If there was already an opened file, then the previous file
        /// is closed first.</para>
        /// 
        /// <para>This method will ensure that the directory structure
        /// for the <paramref name="fileName"/> specified exists.</para>
        /// </remarks>
        virtual protected void OpenFile(string fileName, bool append)
        {
            lock (this)
            {
                Reset();

                // Ensure that the directory structure exists
                Directory.CreateDirectory((new FileInfo(fileName)).DirectoryName);

                //SetQWForFiles(new StreamWriter(fileName, append, m_encoding));
                m_qtw = new CountingQuietTextWriter(fileName,append, FlushTimeSpan);

                m_fileName = fileName;
                m_appendToFile = append;
            }
        }

        /// <summary>
        /// Sets the quiet writer being used.
        /// </summary>
        /// <remarks>
        /// This method can be overridden by sub classes.
        /// </remarks>
        /// <param name="writer">the writer to set</param>
        virtual protected void SetQWForFiles(TextWriter writer)
        {
            //m_qtw = new QuietTextWriter(writer,FlushTimeSpan);
        }
        #endregion Protected Instance Methods

        #region 静态方法

        /// <summary>
        /// Convert a path into a fully qualified path.
        /// </summary>
        /// <param name="path">The path to convert.</param>
        /// <remarks>
        /// <para>
        /// Converts the path specified to a fully
        /// qualified path. If the path is relative it is
        /// taken as relative from the application base 
        /// directory.
        /// </para>
        /// </remarks>
        /// <returns>The fully qualified path.</returns>
        protected static string ConvertToFullPath(string path)
        {
            if (path == null)
            {
                throw new ArgumentNullException("path");
            }

            if (SystemInfo.ApplicationBaseDirectory != null)
            {
                // Note that Path.Combine will return the second path if it is rooted
                return Path.GetFullPath(Path.Combine(SystemInfo.ApplicationBaseDirectory, path));
            }
            return Path.GetFullPath(path);
        }

        #endregion Protected Static Methods
    }
}
